package com.luming.common.jdk.stream;

import cn.hutool.core.util.StrUtil;
import org.apache.commons.lang3.builder.ToStringExclude;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author hcgao
 * @version 1.0.0
 * @ClassName StreamTest.java
 * @Description TODO
 * @createTime 2021年11月12日 09:11:00
 */
public class StreamTest {



    static List<Person> personList = new ArrayList<Person>();
    private static void initPerson() {
        personList.add(new Person("zhang_san", 8, 3000));
        personList.add(new Person("李四", 18, 5000));
        personList.add(new Person("王五", 28, 7000));
        personList.add(new Person("孙六", 38, 9000));
    }


    /**
     * @Description: TODO(通过 java.util.Collection.stream () 方法用集合创建流)
     * @date 2021/11/12 9:13
     *  TODO  stream和 parallelStream的简单区分：stream是顺序流，由主线程按顺序对流执行操作，
     *    而 parallelStream是并行流，内部以多线程并行执行的方式对流进行操作，但前提是流中的数据处理没有顺序要求。
     *    例如筛选集合中的奇数，两者的处理不同之处：
     */
    @Test
    public void listToStream(){
        List<String> list = Arrays.asList("a", "b", "c");
        // 创建一个顺序流
        Stream<String> stream = list.stream();
        // 创建一个并行流
        Stream<String> parallelStream = list.parallelStream();
    }


    /**
     * @Description: TODO(使用 java.util.Arrays.stream(T[]array)方法用数组创建流)
     * @date 2021/11/12 9:13
     */
    @Test
    public void listToStream2(){
        int[] array={1,3,5,6,8};
        IntStream stream = Arrays.stream(array);
    }

    /**
     * @Description: TODO(使用 Stream的静态方法：of()、iterate()、generate())
     * @date 2021/11/12 9:13
     */
    @Test
    public void listToStream3(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
        stream2.forEach(System.out::println);
        Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
        stream3.forEach(System.out::println);


        // 除了直接创建并行流，还可以通过 parallel()把顺序流转换成并行流：
        List<Integer> list = Arrays.asList(1, 3, 8);
        Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();
    }


    /**
     * TODO 遍历/匹配（foreach/find/match）
     */
    @Test
    public void streamForeach(){
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);

        // 遍历输出符合条件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一个
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        // 匹配任意（适用于并行流）
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();

        // 是否包含符合特定条件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 6);
        System.out.println("匹配第一个值：" + findFirst.get());
        System.out.println("匹配任意一个值：" + findAny.get());
        System.out.println("是否存在大于6的值：" + anyMatch);
    }


    /**
     * TODO 2、按条件匹配filter
     */
    @Test
    public void streamFilter(){
        initPerson();
        List<Person> collect = personList.stream().filter(x -> x.getAge()>18).collect(Collectors.toList());
        System.out.println(collect);
    }


    /**
     * TODO 3、聚合max、min、count
     */
    @Test
    public void streamFilter_max(){
        //获取String集合中最长的元素
        List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "sunliu");
        Comparator<? super String> comparator = Comparator.comparing(String::length);
        Optional<String> max = list.stream().max(comparator);
        System.out.println(max);
    }

    @Test
    public void streamFilter_maxInt(){
        List<Integer> list = Arrays.asList(1, 17, 27, 7);
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 自定义排序
        Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println(max2);
    }
    @Test
    public void streamFilter_maxAge(){
        initPerson();
        Comparator<? super Person> comparator = Comparator.comparingInt(Person::getAge);
        Optional<Person> max = personList.stream().max(comparator);
        System.out.println(max);

        //年龄大于 10的人个数
        long count = personList.stream().filter(x -> x.getAge() > 10).count();
        System.out.println(count);
    }

    /**
     * TODO 4、map与flatMap
     */
    @Test
    public void streamFilter_map(){
        List<Integer> list = Arrays.asList(1, 17, 27, 7);
        List<Integer> collect = list.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println(collect);



        List<String> list1 = Arrays.asList("zhangsan", "lisi", "wangwu", "sunliu");
        List<String> collect1 = list1.stream().map(String::toUpperCase).collect(Collectors.toList());
        List<String> collect2 = list1.stream().map(t -> t.toUpperCase()).collect(Collectors.toList());

        initPerson();
        List<Person> collect22 = personList.stream().peek(x -> x.setAge(x.getSalary()+2000)).collect(Collectors.toList());
        System.out.println(collect);


    }
    @Test
    public void streamFilter_map2(){
        initPerson();
        List<String> collect3 = personList.stream().map(Person::getName).map(StrUtil::toCamelCase).collect(Collectors.toList());
        collect3.forEach(e -> {
            System.out.println(e);
        });

        Map<String, Person> collectMap = personList.stream().peek(x -> x.setName(StrUtil.toCamelCase(x.getName()))).collect(Collectors.toMap(Person::getName, Function.identity()));
        Map<String, Integer> collectMap2 = personList.stream().collect(Collectors.toMap(Person::getName, Person::getSalary));
    }

    //TODO 将两个字符数组合并成一个新的字符数组
    @Test
    public void streamFilter_flatmap(){
        String[] arr = {"z, h, a, n, g", "s, a, n"};
        List<String> list = Arrays.asList(arr);
        System.out.println(list);
        List<String> collect = list.stream().flatMap(x -> {
            String[] array = x.split(",");
            Stream<String> stream = Arrays.stream(array);
            return stream;
        }).collect(Collectors.toList());
        System.out.println(collect);
    }

    //TODO 将两个字符数组合并成一个新的字符数组
    @Test
    public void streamFilter_flatmap2(){
        String[] arr = {"z, h, a, n, g", "s, a, n"};
        List<String> list = Arrays.asList(arr);
        List<String> collect = list.stream().flatMap(x -> {
            String[] array = x.split(",");
            Stream<String> stream = Arrays.stream(array);
            return stream;
        }).collect(Collectors.toList());
        System.out.println(collect);
    }






    /**
     * TODO 5、规约 reduce  归约，也称缩减，顾名思义，是把一个流缩减成一个值，能实现对集合求和、求乘积和求最值操作。
     *
     */
    // TODO 求Integer集合的元素之和、乘积和最大值
    @Test
    public void streamFilter_reduce(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4);
        //求和
        Optional<Integer> reduce = list.stream().reduce((x,y) -> x+ y);
        System.out.println("求和:"+reduce);
        //求积
        Optional<Integer> reduce2 = list.stream().reduce((x,y) -> x * y);
        System.out.println("求积:"+reduce2);
        //求最大值
        Optional<Integer> reduce3 = list.stream().reduce((x,y) -> x>y?x:y);
        System.out.println("求最大值:"+reduce3);
    }


    // TODO         求所有员工的工资之和和最高工资
    @Test
    public void streamFilter_reduce2(){
        initPerson();
        Optional<Integer> reduce = personList.stream().map(Person :: getSalary).reduce(Integer::sum);
        Optional<Integer> reduce2 = personList.stream().map(Person :: getSalary).reduce(Integer::max);
        System.out.println("工资之和:"+reduce);
        System.out.println("最高工资:"+reduce2);
    }



    /**
     * TODO 6、收集（toList、toSet、toMap）
     *
     */
    @Test
    public void streamFilter_toMap(){
        initPerson();
        Map<String, Person> collect = personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toMap(Person::getName, y -> y));
        System.out.println(collect);
    }

    /**
     * TODO 7、collect
     *Collectors提供了一系列用于数据统计的静态方法：
     *
     * 计数： count
     * 平均值： averagingInt、 averagingLong、 averagingDouble
     * 最值： maxBy、 minBy
     * 求和： summingInt、 summingLong、 summingDouble
     * 统计以上所有： summarizingInt、 summarizingLong、 summarizingDouble
     */
    //  TODO 统计员工人数、平均工资、工资总额、最高工资
    @Test
    public void streamFilter_collect(){
        initPerson();
        //统计员工人数
        Long count = personList.stream().collect(Collectors.counting());
        //求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        //求最高工资
        Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
        //求工资之和
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        //一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("统计员工人数:"+count);
        System.out.println("求平均工资:"+average);
        System.out.println("求最高工资:"+max);
        System.out.println("求工资之和:"+sum);
        System.out.println("一次性统计所有信息:"+collect);
    }



    /**
     * TODO 8、分组(partitioningBy/groupingBy)
     */
    //  TODO 统计员工人数、平均工资、工资总额、最高工资
    @Test
    public void streamFilter_partitioningBy(){
        personList.add(new Person("zhangsan",25, 3000, "male", "tieling"));
        personList.add(new Person("lisi",27, 5000, "male", "tieling"));
        personList.add(new Person("wangwu",29, 7000, "female", "tieling"));
        personList.add(new Person("sunliu",26, 3000, "female", "dalian"));
        personList.add(new Person("yinqi",27, 5000, "male", "dalian"));
        personList.add(new Person("guba",21, 7000, "female", "dalian"));

        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况：" + part);
        System.out.println("员工按性别分组情况：" + group);
        System.out.println("员工按性别、地区：" + group2);
    }

    /**
     * TODO  9、连接 joining
     */
    @Test
    public void streamFilter_joining(){

        personList.add(new Person("zhangsan",25, 3000, "male", "tieling"));
        personList.add(new Person("lisi",27, 5000, "male", "tieling"));
        personList.add(new Person("wangwu",29, 7000, "female", "tieling"));
        personList.add(new Person("sunliu",26, 3000, "female", "dalian"));
        personList.add(new Person("yinqi",27, 5000, "male", "dalian"));
        personList.add(new Person("guba",21, 7000, "female", "dalian"));

        String collect = personList.stream().map(Person::getName).collect(Collectors.joining("-"));
        System.out.println(collect);
    }


    /**
     * TODO  10、排序  sorted
     */
    @Test
    public void streamFilter_sorted(){

        // 按工资升序排序（自然排序）
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄自定义排序（降序）
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());

        System.out.println("按工资升序排序：" + newList);
        System.out.println("按工资降序排序：" + newList2);
        System.out.println("先按工资再按年龄升序排序：" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序：" + newList4);
    }

    /**
     * TODO  11、提取/组合
     */
    @Test
    public void streamFilter_concat(){
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct：去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit：限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip：跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

        System.out.println("流合并：" + newList);
        System.out.println("limit：" + collect);
        System.out.println("skip：" + collect2);







        //  计算两个list中的差集
       //  List<String> reduce1 = allList.stream().filter(item -> !wList.contains(item)).collect(Collectors.toList());
    }


















}
